<?php

namespace app\common\behavior;

use think\facade\Cache;
use think\Db;
use think\facade\Log;
use util\Logger;

class SqlAutoExecute
{
    // 缓存键名
    private const TABLE_CHECK_CACHE_KEY = 'sql_files_table_exists';

    // 行为入口方法
    public function run()
    {
        $this->checkAndExecuteSqlFiles();
    }

    // 检查并执行SQL文件
    private function checkAndExecuteSqlFiles()
    {
        $sqlDir = ROOT_PATH . 'sql' . DIRECTORY_SEPARATOR . 'dev_sql' . DIRECTORY_SEPARATOR;

        // 检查SQL目录是否存在
        if (!is_dir($sqlDir)) {
            return;
        }
        try {
            // 检查数据表是否存在并缓存结果
            if (!$this->checkTableExists()) {
                // 表不存在，创建表
                $this->createExecutedSqlFilesTable();
                // 创建表后设置缓存
                Cache::set(self::TABLE_CHECK_CACHE_KEY, true, 0);
            }

            // 获取已执行的SQL文件列表
            $executedFiles = Db::name('migrations')->column('file_name');
            // 扫描目录获取所有SQL文件
            $files = glob($sqlDir . '*.sql');
            foreach ($files as $file) {
                $fileName = basename($file);
                // 如果文件未执行过
                if (!in_array($fileName, $executedFiles)) {
                    try {
                        // 执行SQL文件（不使用事务，因为SQL文件可能包含隐式提交的语句）
                        $this->executeSqlFile($file);
                        // 记录已执行（单独事务，确保记录成功）
                        Db::startTrans();
                        try {
                            Db::name('migrations')->insert([
                                'file_name'   => $fileName,
                                'executed_at' => date('Y-m-d H:i:s')
                            ]);
                            Db::commit();
                            Log::info("执行SQL文件: {$fileName} 成功");
                            // 执行成功，清除缓存
                            \util\File::del_dir(ROOT_PATH . 'runtime' . DIRECTORY_SEPARATOR . 'cache');
                            Cache::clear();
                        } catch (\Exception $e) {
                            Db::rollback();
                            Log::error("记录SQL文件执行状态失败: {$fileName} - " . $e->getMessage());
                        }
                    } catch (\Exception $e) {
                        Log::error("执行SQL文件: {$fileName} 失败: " . $e->getMessage());
                    }
                }
            }
        } catch (\Exception $e) {
            $log = [
                'error' => $e->getMessage(),
                'file'  => $e->getFile(),
                'line'  => $e->getLine(),
            ];
            Logger::error('检查SQL文件过程中出错', $log);
        }
    }

    // 检查表是否存在，使用缓存优化
    private function checkTableExists()
    {
        // 先检查缓存
        $cacheResult = Cache::get(self::TABLE_CHECK_CACHE_KEY);
        if ($cacheResult) {
            return $cacheResult;
        }
        // 缓存不存在，查询数据库
        $exists = Db::query("SHOW TABLES LIKE 'yjc_migrations'");
        if ($exists) {
            // 设置缓存
            Cache::set(self::TABLE_CHECK_CACHE_KEY, true, 0);
        }
        return !empty($exists);
    }

    // 创建已执行SQL文件记录表
    private function createExecutedSqlFilesTable()
    {
        $sql = <<<SQL
CREATE TABLE `yzn_migrations` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `file_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'SQL文件名',
  `executed_at` datetime NOT NULL COMMENT '执行时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `file_name` (`file_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='已执行的SQL文件记录';
SQL;

        Db::execute($sql);
        Log::info('创建已执行SQL文件记录表成功');
    }

    // 执行SQL文件内容
    private function executeSqlFile($filePath)
    {
        // 读取SQL文件内容
        $sqlContent = file_get_contents($filePath);

        // 按分号分割SQL语句，考虑引号和注释中的分号
        $sqlStatements = $this->splitSqlStatements($sqlContent);

        // 执行每条SQL语句
        foreach ($sqlStatements as $sql) {
            $sql = trim($sql);
            if (!empty($sql) && !$this->isComment($sql)) {
                Db::execute($sql);
            }
        }
    }

    // 分割SQL语句，处理引号和注释中的分号
    private function splitSqlStatements($sqlContent)
    {
        $statements = [];
        $sql        = '';
        $inQuotes   = false;
        $quoteChar  = '';
        $inComment  = false;

        $length = strlen($sqlContent);
        for ($i = 0; $i < $length; $i++) {
            $char = $sqlContent[$i];

            // 处理注释
            if (!$inQuotes) {
                if (substr($sqlContent, $i, 2) === '--') {
                    $inComment = true;
                    $i++; // 跳过第二个字符
                } elseif (substr($sqlContent, $i, 2) === '/*') {
                    $inComment = true;
                    $i++; // 跳过第二个字符
                }

                if ($inComment) {
                    if (substr($sqlContent, $i, 2) === '*/') {
                        $inComment = false;
                        $i++; // 跳过第二个字符
                    } elseif ($char === "\n") {
                        $inComment = false;
                    }
                    continue;
                }
            }

            // 处理引号
            if (!$inComment) {
                if ($char === "'" || $char === '"') {
                    if (!$inQuotes) {
                        $inQuotes  = true;
                        $quoteChar = $char;
                    } elseif ($inQuotes && $quoteChar === $char) {
                        // 检查是否是转义的引号
                        $escaped = false;
                        $j       = $i - 1;
                        while ($j >= 0 && $sqlContent[$j] === '\\') {
                            $escaped = !$escaped;
                            $j--;
                        }
                        if (!$escaped) {
                            $inQuotes = false;
                        }
                    }
                }
            }

            // 处理分号
            if (!$inQuotes && !$inComment && $char === ';') {
                $statements[] = $sql;
                $sql          = '';
                continue;
            }

            $sql .= $char;
        }

        // 添加最后一条语句（可能没有分号结尾）
        if (!empty($sql)) {
            $statements[] = $sql;
        }

        return $statements;
    }

    // 判断是否是注释
    private function isComment($sql)
    {
        $sql = ltrim($sql);
        return str_starts_with($sql, '--') || str_starts_with($sql, '/*');
    }
}
